﻿using Silk.NET.Input;
using Silk.NET.SDL;
using System;
using System.Text;

namespace OpenGLLib
{
    public sealed unsafe class SilkSDLUtils : IDisposable
    {
        private Sdl sdl;

        private Window* pWin;
        private Renderer* pRenderer;
        private Texture* pTexture;
        private int width;
        private int height;
        //private Mutex* pMutex;
        //private Semaphore* pSemaphore;

        public bool Disposed { get; private set; }

        delegate uint sdlCB(uint a, void* b);

        public SilkSDLUtils()
        {
            sdl = Sdl.GetApi();
            sdl.Init(Sdl.InitEverything);
            //pMutex = sdl.CreateMutex();
            //pSemaphore = sdl.CreateSemaphore(1);
        }

        public bool CreateWin(int width, int height, string title)
        {
            bool rs = sdl.CreateSdlWin(width, height, title, out pWin, out pRenderer, out pTexture);
            //sdl.CreateThread
            System.Threading.Tasks.Task.Factory.StartNew(HandleEvent);
            return rs;
        }

        /// <summary>
        /// 监听事件
        /// </summary>
        private void HandleEvent()
        {
            bool exit = false;
            Event pEvent;
            while (sdl.PollEvent(&pEvent) > -1)
            {
                switch ((EventType)pEvent.Type)
                {
                    case EventType.Keyup:
                        exit = pEvent.Key.Keysym.Sym == (int)Key.Escape;
                        break;
                    case EventType.Quit:
                        exit = true;
                        break;
                    case EventType.Windowevent:
                        width = pEvent.Window.Data1;
                        height = pEvent.Window.Data2;
                        break;
                    default: break;
                }
                if (exit) { break; }
            }
            Dispose();
        }

        public void Show() => sdl.ShowWindow(pWin);

        public void UpdateYuv(byte*[] ptrs, int[] lines)
        {
            //int lockRs = -1;
            //int semRs = -1;
            try
            {
                //lockRs = sdl.TryLockMutex(pMutex);
                //semRs = sdl.SemTryWait(pSemaphore);

                //Silk.NET.Maths.Rectangle<int> rect = new Silk.NET.Maths.Rectangle<int>();
                //rect.Size.X = width;
                //rect.Size.Y = height;

                lock (this)
                {
                    byte* yAddr = ptrs[0];
                    byte* uAddr = ptrs[1];
                    byte* vAddr = ptrs[2];
                    sdl.UpdateYUVTexture(pTexture, null, yAddr, lines[0], uAddr, lines[1], vAddr, lines[2]);
                    sdl.RenderClear(pRenderer);
                    sdl.RenderCopy(pRenderer, pTexture, null, null);
                    sdl.RenderPresent(pRenderer);
                }
            }
            catch { }
            //finally
            //{
            //    //if (lockRs > 0) { sdl.UnlockMutex(pMutex); }
            //    //if (semRs > 0) { sdl.SemPost(pSemaphore); }
            //}
        }

        private bool disposedValue;

        protected void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                Disposed = true;
                if (disposing)
                {
                    // TODO: 释放托管状态(托管对象)
                    //if (pMutex != null)
                    //    sdl.DestroyMutex(pMutex);
                    //if (pSemaphore != null)
                    //    sdl.DestroySemaphore(pSemaphore);
                    sdl.DestroyAll(pWin, pRenderer, pTexture);
                    sdl.Quit();
                    sdl.Dispose();
                }
                // TODO: 释放未托管的资源(未托管的对象)并重写终结器
                // TODO: 将大型字段设置为 null
                disposedValue = true;
            }
        }

        public void Dispose()
        {
            // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中
            Dispose(disposing: true);
            GC.SuppressFinalize(this);
        }

    }

    public static unsafe class SilkSDLUtilsExtensions
    {
        public static bool CreateSdlWin(this Sdl sdl, int width, int height, string title, out Window* pWin, out Renderer* pRenderer, out Texture* pTexture)
        {
            byte* pTitle = null;
            if (!string.IsNullOrWhiteSpace(title))
            {
                byte[] tarr = Encoding.UTF8.GetBytes(title);
                void* pvoid = System.Runtime.CompilerServices.Unsafe.AsPointer(ref tarr);
                pTitle = (byte*)pvoid;
            }
            uint flag = (uint)WindowFlags.WindowOpengl | (uint)WindowFlags.WindowResizable;

            pWin = sdl.CreateWindow(pTitle, Sdl.WindowposCentered, Sdl.WindowposCentered, width, height, flag);
            if (pWin == null)
                throw sdl.GetErrorAsException();

            uint renderFlag = (uint)RendererFlags.RendererAccelerated;
            pRenderer = sdl.CreateRenderer(pWin, -1, renderFlag);
            if (pRenderer == null)
                throw sdl.GetErrorAsException();

            int access = (int)TextureAccess.TextureaccessStreaming;
            pTexture = sdl.CreateTexture(pRenderer, Sdl.PixelformatIyuv, access, width, height);
            if (pTexture == null)
                throw sdl.GetErrorAsException();

            sdl.SetRenderTarget(pRenderer, pTexture);
            return true;
        }

        public static void DestroyAll(this Sdl sdl, Window* pWin, Renderer* pRenderer, Texture* pTexture)
        {
            if (pWin != null)
                sdl.DestroyWindow(pWin);

            if (pRenderer != null)
                sdl.DestroyRenderer(pRenderer);

            if (pTexture != null)
                sdl.DestroyTexture(pTexture);
        }

    }
}